---
title: "Python Object-Oriented & Functional Programming"
description: "Learn Python's object-oriented programming, functional programming features, and decorators from a JavaScript developer's perspective."
---

## 1. Introduction

### Why Learn Object-Oriented and Functional Programming?

As a JavaScript developer, you are likely familiar with ES6 class syntax and functional programming concepts. Python has its own unique implementations of these paradigms. Mastering these features will enable you to:

-   Write more elegant and maintainable code
-   Understand Python's design philosophy
-   Leverage Python's powerful metaprogramming capabilities
-   Build more complex applications

> 💡 **Learning Strategy**: Think of Python's OOP and functional features as "enhanced versions" of JavaScript concepts.

## 2. Object-Oriented Programming (OOP)

### 2.1 Basic Class Concepts

Python's class system has many similarities to JavaScript's, but also some important differences.

<PythonEditor title="Basic Class Definition Comparison" compare={true}>
```javascript !! js
// JavaScript class definition
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    // Instance method
    sayHello() {
        return `Hello, I am ${this.name}, and I am ${this.age} years old`;
    }
    
    // Static method
    static create(name, age) {
        return new Person(name, age);
    }
    
    // Getter
    get info() {
        return `${this.name} (${this.age})`;
    }
    
    // Setter
    set info(value) {
        const [name, age] = value.split(' ');
        this.name = name;
        this.age = parseInt(age);
    }
}

// Using the class
const person = new Person("John Doe", 25);
console.log(person.sayHello());
console.log(person.info);
person.info = "Jane Doe 30";
console.log(person.name); // "Jane Doe"
```

```python !! py
# Python class definition
class Person:
    """Person class"""
    
    def __init__(self, name, age):
        """Constructor (similar to JavaScript's constructor)"""
        self.name = name
        self.age = age
    
    def say_hello(self):
        """Instance method (similar to JavaScript's instance method)"""
        return f"Hello, I am {self.name}, and I am {self.age} years old"
    
    @staticmethod
    def a_static_method():
        """Static method, cannot access class or instance"""
        return "This is a static method"
    
    @classmethod
    def create(cls, name, age):
        """Class method (similar to JavaScript's static method)"""
        return cls(name, age)
    
    @property
    def info(self):
        """Property decorator (similar to JavaScript's getter)"""
        return f"{self.name} ({self.age})"
    
    @info.setter
    def info(self, value):
        """Property setter (similar to JavaScript's setter)"""
        name, age = value.split(' ')
        self.name = name
        self.age = int(age)
    
    def __str__(self):
        """String representation (similar to JavaScript's toString)"""
        return f"Person({self.name}, {self.age})"

# Using the class
person = Person("John Doe", 25)
print(person.say_hello())
print(person.info)
person.info = "Jane Doe 30"
print(person.name)  # "Jane Doe"
print(person)  # Person(Jane Doe, 30)
```
</PythonEditor>

### 2.2 Class Variables vs. Instance Variables

The concepts of class variables and instance variables in Python are similar to static properties and instance properties in JavaScript.

<PythonEditor title="Class Variables vs. Instance Variables" compare={true}>
```javascript !! js
// JavaScript class variables and instance variables
class Counter {
    // Class variable (static property)
    static totalCount = 0;
    static instances = [];
    
    constructor() {
        // Instance variable
        this.count = 0;
        Counter.totalCount++;
        Counter.instances.push(this);
    }
    
    increment() {
        this.count++;
        Counter.totalCount++;
    }
    
    static getTotalCount() {
        return Counter.totalCount;
    }
    
    static getAllInstances() {
        return Counter.instances;
    }
}

const counter1 = new Counter();
const counter2 = new Counter();

counter1.increment();
counter2.increment();
counter2.increment();

console.log(counter1.count);           // 1
console.log(counter2.count);           // 2
console.log(Counter.getTotalCount());  // 3
console.log(Counter.getAllInstances().length); // 2
```

```python !! py
# Python class variables and instance variables
class Counter:
    """Counter class"""
    
    # Class variable (similar to JavaScript's static property)
    total_count = 0
    instances = []
    
    def __init__(self):
        """Constructor"""
        # Instance variable
        self.count = 0
        Counter.total_count += 1
        Counter.instances.append(self)
    
    def increment(self):
        """Increment the count"""
        self.count += 1
        Counter.total_count += 1
    
    @classmethod
    def get_total_count(cls):
        """Get the total count (class method)"""
        return cls.total_count
    
    @classmethod
    def get_all_instances(cls):
        """Get all instances (class method)"""
        return cls.instances

# Create instances
counter1 = Counter()
counter2 = Counter()

counter1.increment()
counter2.increment()
counter2.increment()

print(counter1.count)                    # 1
print(counter2.count)                    # 2
print(Counter.get_total_count())         # 3
print(len(Counter.get_all_instances()))  # 2
```
</PythonEditor>

### 2.3 Inheritance and Polymorphism

Python's inheritance system is more intuitive and powerful than JavaScript's.

<PythonEditor title="Inheritance and Polymorphism" compare={true}>
```javascript !! js
// JavaScript inheritance
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        return `${this.name} makes a sound`;
    }
    
    move() {
        return `${this.name} is moving`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    
    speak() {
        return `${this.name} barks`;
    }
    
    fetch() {
        return `${this.name} is fetching`;
    }
}

class Cat extends Animal {
    constructor(name, color) {
        super(name);
        this.color = color;
    }
    
    speak() {
        return `${this.name} meows`;
    }
    
    climb() {
        return `${this.name} is climbing`;
    }
}

// Polymorphism example
const animals = [
    new Dog("Buddy", "Golden Retriever"),
    new Cat("Mimi", "Orange")
];

animals.forEach(animal => {
    console.log(animal.speak());
    console.log(animal.move());
});
```

```python !! py
# Python inheritance
class Animal:
    """Animal base class"""
    
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        """Make a sound"""
        return f"{self.name} makes a sound"
    
    def move(self):
        """Move"""
        return f"{self.name} is moving"

class Dog(Animal):
    """Dog class"""
    
    def __init__(self, name, breed):
        super().__init__(name)  # Call the parent class constructor
        self.breed = breed
    
    def speak(self):
        """Override the parent class method"""
        return f"{self.name} barks"
    
    def fetch(self):
        """Dog-specific method"""
        return f"{self.name} is fetching"

class Cat(Animal):
    """Cat class"""
    
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color
    
    def speak(self):
        """Override the parent class method"""
        return f"{self.name} meows"
    
    def climb(self):
        """Cat-specific method"""
        return f"{self.name} is climbing"

# Polymorphism example
animals = [
    Dog("Buddy", "Golden Retriever"),
    Cat("Mimi", "Orange")
]

for animal in animals:
    print(animal.speak())
    print(animal.move())
```
</PythonEditor>

### 2.4 Special Methods (Magic Methods)

Python's special methods are a powerful feature of its object-oriented programming, similar to Symbol methods in JavaScript.

<PythonEditor title="Special Method Example" compare={true}>
```javascript !! js
// JavaScript special methods (using Symbol)
class Vector {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    // Addition operation
    add(other) {
        return new Vector(this.x + other.x, this.y + other.y);
    }
    
    // String representation
    toString() {
        return `Vector(${this.x}, ${this.y})`;
    }
    
    // Equality comparison
    equals(other) {
        return this.x === other.x && this.y === other.y;
    }
    
    // Array destructuring
    [Symbol.iterator]() {
        return [this.x, this.y][Symbol.iterator]();
    }
}

const v1 = new Vector(1, 2);
const v2 = new Vector(3, 4);
const v3 = v1.add(v2);

console.log(v1.toString());  // "Vector(1, 2)"
console.log(v3.toString());  // "Vector(4, 6)"
console.log(v1.equals(v2));  // false

// Destructuring assignment
const [x, y] = v1;
console.log(x, y);  // 1, 2
```

```python !! py
# Python special methods
class Vector:
    """Vector class"""
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        """Addition operation (+ operator)"""
        return Vector(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        """Subtraction operation (- operator)"""
        return Vector(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):
        """Multiplication operation (* operator)"""
        return Vector(self.x * scalar, self.y * scalar)
    
    def __eq__(self, other):
        """Equality comparison (== operator)"""
        return self.x == other.x and self.y == other.y
    
    def __str__(self):
        """String representation (str() function)"""
        return f"Vector({self.x}, {self.y})"
    
    def __repr__(self):
        """Detailed string representation (repr() function)"""
        return f"Vector({self.x}, {self.y})"
    
    def __len__(self):
        """Length (len() function)"""
        return 2
    
    def __getitem__(self, index):
        """Index access ([] operator)"""
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        else:
            raise IndexError("Vector has only two components")
    
    def __iter__(self):
        """Iterator (for loop)"""
        return iter([self.x, self.y])

# Using special methods
v1 = Vector(1, 2)
v2 = Vector(3, 4)

print(v1)                    # Vector(1, 2)
print(v1 + v2)              # Vector(4, 6)
print(v2 - v1)              # Vector(2, 2)
print(v1 * 3)               # Vector(3, 6)
print(v1 == v2)             # False
print(len(v1))              # 2
print(v1[0], v1[1])         # 1, 2

# Iteration
for component in v1:
    print(component)         # 1, 2
```
</PythonEditor>

## 3. Functional Programming Features

### 3.1 Higher-Order Functions

Python supports higher-order functions, similar to functional programming concepts in JavaScript.

<PythonEditor title="Higher-Order Function Example" compare={true}>
```javascript !! js
// JavaScript higher-order functions
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map - transform an array
const doubled = numbers.map(x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

// filter - filter an array
const evens = numbers.filter(x => x % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]

// reduce - reduce an array
const sum = numbers.reduce((acc, x) => acc + x, 0);
console.log(sum); // 55

// Chaining
const result = numbers
    .filter(x => x % 2 === 0)
    .map(x => x * 2)
    .reduce((acc, x) => acc + x, 0);
console.log(result); // 60

// Function as an argument
function applyOperation(numbers, operation) {
    return numbers.map(operation);
}

const squared = applyOperation(numbers, x => x ** 2);
console.log(squared); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```

```python !! py
# Python higher-order functions
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map - transform a sequence
doubled = list(map(lambda x: x * 2, numbers))
print(doubled)  # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# filter - filter a sequence
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4, 6, 8, 10]

# reduce - reduce a sequence
from functools import reduce
sum_result = reduce(lambda acc, x: acc + x, numbers, 0)
print(sum_result)  # 55

# Chaining
result = reduce(
    lambda acc, x: acc + x,
    map(lambda x: x * 2, filter(lambda x: x % 2 == 0, numbers)),
    0
)
print(result)  # 60

# Function as an argument
def apply_operation(numbers, operation):
    """Apply an operation to a list of numbers"""
    return list(map(operation, numbers))

squared = apply_operation(numbers, lambda x: x ** 2)
print(squared)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```
</PythonEditor>

### 3.2 Lambda Functions

Python's lambda functions are similar to JavaScript's arrow functions, but more limited in functionality.

<PythonEditor title="Lambda Function Comparison" compare={true}>
```javascript !! js
// JavaScript arrow functions
const add = (a, b) => a + b;
const square = x => x * x;
const isEven = x => x % 2 === 0;

// Multi-line arrow function
const processNumber = x => {
    const squared = x * x;
    return squared > 10 ? squared : 0;
};

// Immediately invoked function
const result = ((x, y) => x + y)(5, 3);
console.log(result); // 8

// Using in array methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
const evens = numbers.filter(x => x % 2 === 0);
const sum = numbers.reduce((acc, x) => acc + x, 0);

console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evens);   // [2, 4]
console.log(sum);     // 15
```

```python !! py
# Python lambda functions
add = lambda a, b: a + b
square = lambda x: x * x
is_even = lambda x: x % 2 == 0

# Lambda functions can only contain a single expression
# Cannot contain multi-line code or complex logic

# Immediately invoked function
result = (lambda x, y: x + y)(5, 3)
print(result)  # 8

# Using in functional programming
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
sum_result = reduce(lambda acc, x: acc + x, numbers, 0)

print(doubled)    # [2, 4, 6, 8, 10]
print(evens)      # [2, 4]
print(sum_result) # 15

# Limitations of lambda functions
# Cannot contain assignment statements
# Cannot contain conditional statements (but can use ternary operators)
process_number = lambda x: x * x if x > 2 else 0
```
</PythonEditor>

### 3.3 List Comprehensions

List comprehensions are a feature of Python, similar to a combination of map and filter in JavaScript.

<PythonEditor title="List Comprehension Comparison" compare={true}>
```javascript !! js
// JavaScript array methods
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Basic mapping
const doubled = numbers.map(x => x * 2);

// Conditional filtering
const evens = numbers.filter(x => x % 2 === 0);

// Combined operation
const result = numbers
    .filter(x => x % 2 === 0)
    .map(x => x * 2);

// Nested array processing
const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const flattened = matrix.flat();
const doubledMatrix = matrix.map(row => row.map(x => x * 2));

console.log(doubled);      // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
console.log(evens);        // [2, 4, 6, 8, 10]
console.log(result);       // [4, 8, 12, 16, 20]
console.log(flattened);    // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(doubledMatrix); // [[2, 4, 6], [8, 10, 12], [14, 16, 18]]
```

```python !! py
# Python list comprehensions
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Basic mapping
doubled = [x * 2 for x in numbers]

# Conditional filtering
evens = [x for x in numbers if x % 2 == 0]

# Combined operation (filtering + mapping)
result = [x * 2 for x in numbers if x % 2 == 0]

# Nested list processing
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [x for row in matrix for x in row]
doubled_matrix = [[x * 2 for x in row] for row in matrix]

print(doubled)        # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
print(evens)          # [2, 4, 6, 8, 10]
print(result)         # [4, 8, 12, 16, 20]
print(flattened)      # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(doubled_matrix) # [[2, 4, 6], [8, 10, 12], [14, 16, 18]]

# Dictionary comprehensions
squares = {x: x ** 2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Set comprehensions
unique_squares = {x ** 2 for x in [1, 2, 2, 3, 3, 4]}
print(unique_squares)  # {1, 4, 9, 16}
```
</PythonEditor>

## 4. Decorators

Decorators are a unique feature of Python, similar to higher-order components or middleware in JavaScript.

### 4.1 Basic Decorators

<PythonEditor title="Basic Decorator Example" compare={true}>
```javascript !! js
// JavaScript higher-order function (similar to a decorator)
function logFunction(func) {
    return function(...args) {
        console.log(`Calling function: ${func.name}`);
        console.log(`Arguments:`, args);
        const result = func(...args);
        console.log(`Result:`, result);
        return result;
    };
}

function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

// Apply the decorator
const loggedAdd = logFunction(add);
const loggedMultiply = logFunction(multiply);

loggedAdd(5, 3);
loggedMultiply(4, 2);

// Using decorator syntax (requires Babel plugin)
// @logFunction
// function add(a, b) {
//     return a + b;
// }
```

```python !! py
# Python decorator
def log_function(func):
    """Logging decorator"""
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        print(f"Arguments: {args}, {kwargs}")
        result = func(*args, **kwargs)
        print(f"Result: {result}")
        return result
    return wrapper

# Using decorator syntax
@log_function
def add(a, b):
    """Addition function"""
    return a + b

@log_function
def multiply(a, b):
    """Multiplication function"""
    return a * b

# Call the function
add(5, 3)
multiply(4, 2)

# Equivalent to:
# add = log_function(add)
# multiply = log_function(multiply)
```
</PythonEditor>

### 4.2 Decorators with Arguments

<PythonEditor title="Decorators with Arguments" compare={true}>
```javascript !! js
// JavaScript higher-order function with arguments
function retry(maxAttempts = 3, delay = 1000) {
    return function(func) {
        return function(...args) {
            let lastError;
            
            for (let attempt = 1; attempt <= maxAttempts; attempt++) {
                try {
                    console.log(`Attempt ${attempt}`);
                    return func(...args);
                } catch (error) {
                    lastError = error;
                    console.log(`Attempt ${attempt} failed:`, error.message);
                    
                    if (attempt < maxAttempts) {
                        console.log(`Retrying after ${delay}ms...`);
                        // Use setTimeout in a real environment
                    }
                }
            }
            
            throw lastError;
        };
    };
}

function unreliableFunction() {
    if (Math.random() < 0.7) {
        throw new Error("Random failure");
    }
    return "Success!";
}

// Apply the decorator
const retryFunction = retry(3, 1000)(unreliableFunction);
// retryFunction();
```

```python !! py
# Python decorator with arguments
import time
import random

def retry(max_attempts=3, delay=1):
    """Retry decorator"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            last_error = None
            
            for attempt in range(1, max_attempts + 1):
                try:
                    print(f"Attempt {attempt}")
                    return func(*args, **kwargs)
                except Exception as error:
                    last_error = error
                    print(f"Attempt {attempt} failed: {error}")
                    
                    if attempt < max_attempts:
                        print(f"Retrying after {delay} second(s)...")
                        time.sleep(delay)
            
            raise last_error
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def unreliable_function():
    """Unreliable function"""
    if random.random() < 0.7:
        raise ValueError("Random failure")
    return "Success!"

# Call the function
try:
    result = unreliable_function()
    print(f"Final result: {result}")
except Exception as e:
    print(f"All attempts failed: {e}")
```
</PythonEditor>

### 4.3 Class Decorators

<PythonEditor title="Class Decorator Example" compare={true}>
```javascript !! js
// JavaScript class decorator (conceptual)
function singleton(Class) {
    let instance = null;
    
    return class extends Class {
        constructor(...args) {
            if (instance) {
                return instance;
            }
            super(...args);
            instance = this;
        }
    };
}

// Using the decorator
// @singleton
// class Database {
//     constructor() {
//         this.connection = "Database connection";
//     }
// }

// Manual application
class Database {
    constructor() {
        this.connection = "Database connection";
    }
}

const SingletonDatabase = singleton(Database);
const db1 = new SingletonDatabase();
const db2 = new SingletonDatabase();
console.log(db1 === db2); // true
```

```python !! py
# Python class decorator
def singleton(cls):
    """Singleton decorator"""
    instances = {}
    
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class Database:
    """Database class"""
    
    def __init__(self):
        self.connection = "Database connection"
        print("Creating database connection")
    
    def query(self, sql):
        return f"Executing query: {sql}"

# Test singleton
db1 = Database()
db2 = Database()
print(db1 is db2)  # True

print(db1.query("SELECT * FROM users"))
print(db2.query("SELECT * FROM products"))
```
</PythonEditor>

## 5. Practical Project Example

### 5.1 Data Validator

Let's create a data validator that combines object-oriented and functional programming.

<PythonEditor title="Data Validator Example" compare={true}>
```javascript !! js
// JavaScript data validator
class Validator {
    constructor() {
        this.rules = [];
    }
    
    addRule(rule) {
        this.rules.push(rule);
        return this;
    }
    
    validate(data) {
        const errors = [];
        
        for (const rule of this.rules) {
            try {
                rule(data);
            } catch (error) {
                errors.push(error.message);
            }
        }
        
        return {
            isValid: errors.length === 0,
            errors
        };
    }
}

// Validation rules
const isString = (data) => {
    if (typeof data !== 'string') {
        throw new Error('Must be a string');
    }
};

const minLength = (min) => (data) => {
    if (data.length < min) {
        throw new Error(`Length cannot be less than ${min} characters`);
    }
};

const maxLength = (max) => (data) => {
    if (data.length > max) {
        throw new Error(`Length cannot be more than ${max} characters`);
    }
};

const pattern = (regex) => (data) => {
    if (!regex.test(data)) {
        throw new Error('Invalid format');
    }
};

// Using the validator
const userValidator = new Validator()
    .addRule(isString)
    .addRule(minLength(3))
    .addRule(maxLength(20))
    .addRule(pattern(/^[a-zA-Z0-9_]+$/));

const result = userValidator.validate("john_doe");
console.log(result);
```

```python !! py
# Python data validator
from typing import Callable, List, Dict, Any
import re

class Validator:
    """Data validator class"""
    
    def __init__(self):
        self.rules: List[Callable] = []
    
    def add_rule(self, rule: Callable) -> 'Validator':
        """Add a validation rule"""
        self.rules.append(rule)
        return self
    
    def validate(self, data: Any) -> Dict[str, Any]:
        """Validate data"""
        errors = []
        
        for rule in self.rules:
            try:
                rule(data)
            except ValueError as error:
                errors.append(str(error))
        
        return {
            'is_valid': len(errors) == 0,
            'errors': errors
        }

# Validation rules (functional style)
def is_string(data: Any) -> None:
    """Check if it is a string"""
    if not isinstance(data, str):
        raise ValueError('Must be a string')

def min_length(min_len: int) -> Callable:
    """Minimum length validator"""
    def validator(data: str) -> None:
        if len(data) < min_len:
            raise ValueError(f'Length cannot be less than {min_len} characters')
    return validator

def max_length(max_len: int) -> Callable:
    """Maximum length validator"""
    def validator(data: str) -> None:
        if len(data) > max_len:
            raise ValueError(f'Length cannot be more than {max_len} characters')
    return validator

def pattern(regex: str) -> Callable:
    """Regular expression validator"""
    def validator(data: str) -> None:
        if not re.match(regex, data):
            raise ValueError('Invalid format')
    return validator

# Using the validator
user_validator = (Validator()
    .add_rule(is_string)
    .add_rule(min_length(3))
    .add_rule(max_length(20))
    .add_rule(pattern(r'^[a-zA-Z0-9_]+$')))

result = user_validator.validate("john_doe")
print(result)

# Test invalid data
invalid_result = user_validator.validate("ab")
print(invalid_result)
```
</PythonEditor>

### 5.2 Cache Decorator

<PythonEditor title="Cache Decorator Example" compare={true}>
```javascript !! js
// JavaScript cache decorator
function memoize(func) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            console.log('Getting result from cache');
            return cache.get(key);
        }
        
        console.log('Calculating new result');
        const result = func(...args);
        cache.set(key, result);
        return result;
    };
}

// Using the cache decorator
const expensiveFunction = memoize(function(n) {
    console.log(`Calculating factorial of ${n}...`);
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
});

console.log(expensiveFunction(5));  // Calculating new result
console.log(expensiveFunction(5));  // Getting result from cache
console.log(expensiveFunction(6));  // Calculating new result
```

```python !! py
# Python cache decorator
from functools import wraps
import json
from typing import Any, Dict

def memoize(func):
    """Cache decorator"""
    cache: Dict[str, Any] = {}
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        # Create a cache key
        key = json.dumps((args, sorted(kwargs.items())))
        
        if key in cache:
            print('Getting result from cache')
            return cache[key]
        
        print('Calculating new result')
        result = func(*args, **kwargs)
        cache[key] = result
        return result
    
    return wrapper

@memoize
def expensive_function(n: int) -> int:
    """Expensive function to calculate factorial"""
    print(f'Calculating factorial of {n}...')
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

# Test the cache
print(expensive_function(5))  # Calculating new result
print(expensive_function(5))  # Getting result from cache
print(expensive_function(6))  # Calculating new result

# Using the built-in lru_cache
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    """Fibonacci sequence (with cache)"""
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10))  # Fast calculation, uses cache
```
</PythonEditor>

## 6. Exercises

### Exercise 1: Create a Student Management System

<PythonEditor title="Exercise 1: Student Management System" compare={true}>
```javascript !! js
// Create a student management system
class Student {
    constructor(id, name, age, grades = []) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grades = grades;
    }
    
    addGrade(grade) {
        this.grades.push(grade);
    }
    
    getAverage() {
        if (this.grades.length === 0) return 0;
        return this.grades.reduce((sum, grade) => sum + grade, 0) / this.grades.length;
    }
    
    toString() {
        return `Student(${this.id}, ${this.name}, ${this.age})`;
    }
}

class StudentManager {
    constructor() {
        this.students = new Map();
    }
    
    addStudent(student) {
        this.students.set(student.id, student);
    }
    
    getStudent(id) {
        return this.students.get(id);
    }
    
    getAllStudents() {
        return Array.from(this.students.values());
    }
    
    getTopStudents(limit = 3) {
        return this.getAllStudents()
            .sort((a, b) => b.getAverage() - a.getAverage())
            .slice(0, limit);
    }
    
    getStudentsByAgeRange(minAge, maxAge) {
        return this.getAllStudents()
            .filter(student => student.age >= minAge && student.age <= maxAge);
    }
}

// Example usage
const manager = new StudentManager();

const student1 = new Student(1, "John Doe", 20);
student1.addGrade(85);
student1.addGrade(90);

const student2 = new Student(2, "Jane Doe", 19);
student2.addGrade(92);
student2.addGrade(88);

manager.addStudent(student1);
manager.addStudent(student2);

console.log("All students:", manager.getAllStudents());
console.log("Top students:", manager.getTopStudents(2));
```

```python !! py
# Create a student management system
from typing import List, Dict, Optional
from dataclasses import dataclass, field

@dataclass
class Student:
    """Student class (using dataclass to automatically generate __init__, __repr__, etc.)"""
    id: int
    name: str
    age: int
    grades: List[float] = field(default_factory=list)
    
    def add_grade(self, grade: float) -> None:
        """Add a grade"""
        self.grades.append(grade)
    
    def get_average(self) -> float:
        """Calculate the average grade"""
        if not self.grades:
            return 0.0
        return sum(self.grades) / len(self.grades)
    
    def __str__(self) -> str:
        return f"Student({self.id}, {self.name}, {self.age})"

class StudentManager:
    """Student manager"""
    
    def __init__(self):
        self.students: Dict[int, Student] = {}
    
    def add_student(self, student: Student) -> None:
        """Add a student"""
        self.students[student.id] = student
    
    def get_student(self, student_id: int) -> Optional[Student]:
        """Get a student"""
        return self.students.get(student_id)
    
    def get_all_students(self) -> List[Student]:
        """Get all students"""
        return list(self.students.values())
    
    def get_top_students(self, limit: int = 3) -> List[Student]:
        """Get top students"""
        return sorted(
            self.get_all_students(),
            key=lambda s: s.get_average(),
            reverse=True
        )[:limit]
    
    def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]:
        """Get students by age range"""
        return [
            student for student in self.get_all_students()
            if min_age <= student.age <= max_age
        ]

# Example usage
manager = StudentManager()

student1 = Student(1, "John Doe", 20)
student1.add_grade(85)
student1.add_grade(90)

student2 = Student(2, "Jane Doe", 19)
student2.add_grade(92)
student2.add_grade(88)

manager.add_student(student1)
manager.add_student(student2)

print("All students:", manager.get_all_students())
print("Top students:", manager.get_top_students(2))
```
</PythonEditor>

### Exercise 2: Functional Programming Exercise

<PythonEditor title="Exercise 2: Functional Programming" compare={true}>
```javascript !! js
// Functional programming exercise
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 1. Find the squares of all even numbers
const evenSquares = numbers
    .filter(x => x % 2 === 0)
    .map(x => x * x);

// 2. Calculate the sum of all odd numbers
const oddSum = numbers
    .filter(x => x % 2 === 1)
    .reduce((sum, x) => sum + x, 0);

// 3. Find the cubes of all numbers greater than 5
const largeCubes = numbers
    .filter(x => x > 5)
    .map(x => x ** 3);

console.log("Squares of even numbers:", evenSquares);
console.log("Sum of odd numbers:", oddSum);
console.log("Cubes of numbers greater than 5:", largeCubes);
```

```python !! py
# Functional programming exercise
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 1. Find the squares of all even numbers
even_squares = [x ** 2 for x in numbers if x % 2 == 0]

# 2. Calculate the sum of all odd numbers
odd_sum = sum(x for x in numbers if x % 2 == 1)

# 3. Find the cubes of all numbers greater than 5
large_cubes = [x ** 3 for x in numbers if x > 5]

print("Squares of even numbers:", even_squares)
print("Sum of odd numbers:", odd_sum)
print("Cubes of numbers greater than 5:", large_cubes)

# Using functional methods
from functools import reduce

even_squares_func = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
odd_sum_func = reduce(lambda acc, x: acc + x, filter(lambda x: x % 2 == 1, numbers), 0)
large_cubes_func = list(map(lambda x: x ** 3, filter(lambda x: x > 5, numbers)))

print("Functional method - Squares of even numbers:", even_squares_func)
print("Functional method - Sum of odd numbers:", odd_sum_func)
print("Functional method - Cubes of numbers greater than 5:", large_cubes_func)
```
</PythonEditor>

## 7. Summary

### Key Concept Review

1.  **Object-Oriented Programming**
    -   Class definition and instantiation
    -   Inheritance and polymorphism
    -   Special methods (magic methods)
    -   Class variables and instance variables

2.  **Functional Programming**
    -   Higher-order functions (map, filter, reduce)
    -   Lambda functions
    -   List comprehensions
    -   Functions as arguments and return values

3.  **Decorators**
    -   Basic decorators
    -   Decorators with arguments
    -   Class decorators
    -   Practical use cases

### JavaScript vs. Python Comparison Summary

| Concept | JavaScript | Python | Description |
|---|---|---|---|
| **Class Definition** | `class ClassName` | `class ClassName:` | Python uses a colon. |
| **Constructor** | `constructor()` | `__init__(self)` | Python requires the `self` parameter. |
| **Inheritance** | `extends` | `(ParentClass)` | Python uses parenthesis syntax. |
| **Special Methods** | Symbol methods | `__method__` | Python has more extensive special methods. |
| **Higher-Order Functions** | Built-in methods | `map`, `filter`, `reduce` | Same concept, different syntax. |
| **Anonymous Functions** | Arrow functions | `lambda` | Python's lambda is more limited. |
| **Decorators** | Higher-order functions | `@decorator` | Python has syntax sugar support. |

### Best Practices

1.  **Object-Oriented Programming**
    -   Use `@dataclass` to simplify data classes
    -   Use inheritance and composition appropriately
    -   Implement appropriate special methods
    -   Follow the single responsibility principle

2.  **Functional Programming**
    -   Prefer list comprehensions
    -   Use lambda functions appropriately
    -   Avoid excessive nesting
    -   Maintain function purity

3.  **Decorators**
    -   Use `@wraps` to preserve function metadata
    -   Design decorator arguments appropriately
    -   Be aware of the execution order of decorators
    -   Avoid overusing decorators

### Next Steps

In the next module, we will learn about:
- Python asynchronous programming (async/await)
- Event loop mechanism
- Asynchronous web development
- Basics of concurrent programming

These concepts will help you build high-performance Python applications, especially in web development and data processing.
